#include "VxIrrLib.h"


//============================================================================
//=== constructor ===//
GameLevelMgr::GameLevelMgr()
	: m_poIrrBase( 0 )
	, m_f32GameVersion(0.0f)
	, m_f32LevelVersion(0.0f)
	, m_poSkyBoxNode(0)
{
	memset( m_apoSkyboxTex, 0, sizeof( m_apoSkyboxTex ) );
}
//============================================================================
//=== destructor ===//
GameLevelMgr::~GameLevelMgr()
{
	// empty
}

//============================================================================
//! reset variables from any previous load of a level
void GameLevelMgr::ResetVars( void )
{


}
//============================================================================
//! load and initialize level
RCODE GameLevelMgr::GameLevelStartup( VxIrrBase * poIrrBase, const char * pLevelFileName, BOOL bReloadFromResume )
{
	RCODE rc = 0;
	log_msg( 0, "GameLevelMgr::GameLevelStartup file %s\n", pLevelFileName );

	log_msg( 0, "GameLevelMgr::GameLevelSartup: unloading previous resources\n" );
	UnloadLevelResources();


	m_poIrrBase		= poIrrBase;
	m_poDriver		= m_poIrrBase->m_poDriver;
	m_poSceneMgr	= m_poIrrBase->m_poSceneMgr;

	// load default scripting
	//m_astrScripts.push_back( GetAssetPathAndName("default.nut", eGameAssetTypeScript ) );

	log_msg( 0, "GameLevelMgr::GameLevelStartup loading level\n" );
	m_strLevelFile = GetAssetPathAndName( pLevelFileName, eGameAssetTypeLevel );
	TiXmlDocument oXmlDocument( m_strLevelFile.c_str() );
	if( 0 == oXmlDocument.LoadFile() ) 
	{
		log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR line %d column %d: %s\n", oXmlDocument.ErrorRow(), oXmlDocument.ErrorCol(), oXmlDocument.ErrorDesc());
		GameLevelShutdown();
		return 0;
	}

	TiXmlElement * poLevelElement = oXmlDocument.FirstChildElement("level");
	if( NULL == poLevelElement) 
	{
		log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find level tag\n");
		GameLevelShutdown();
		return 0;
	}

	log_msg( 0, "GameLevelMgr::GameLevelStartup getting level version\n" );
	// get level version
	m_f32LevelVersion = 0.0f;
	if( TIXML_SUCCESS != poLevelElement->QueryFloatAttribute( "levelversion", &m_f32LevelVersion ) ) 
	{
		log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find level version\n");
		GameLevelShutdown();
		return 0;
	}
	if( 0.0f == m_f32LevelVersion ) 
	{
		log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find game version\n");
		GameLevelShutdown();
		return 0;
	}

	// get game version
	log_msg( 0, "GameLevelMgr::GameLevelStartup getting game version\n" );
	m_f32GameVersion = 0.0f;
	if( TIXML_SUCCESS != poLevelElement->QueryFloatAttribute( "gameversion", &m_f32GameVersion ) ) 
	{
		log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find game version\n");
		GameLevelShutdown();
		return 0;
	}
	if( 0 == m_f32GameVersion ) 
	{
		log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find game version\n");
		GameLevelShutdown();
		return 0;
	}
	log_msg( 0, "GameLevelMgr::GameLevelStartup getting skybox\n" );
	// load skybox if any
	TiXmlElement * poSkyboxResource = poLevelElement->FirstChildElement( "skybox" );
	if( poSkyboxResource ) 
	{
		int iIdx = 0;
		TiXmlElement * poSkyboxElement = poSkyboxResource->FirstChildElement("cube_texture");
		if( poSkyboxElement )
		{
			m_astrSkybox[ eCubeSideLeft ]	= poSkyboxElement->Attribute("left");
			m_astrSkybox[ eCubeSideRight ]	= poSkyboxElement->Attribute("right");
			m_astrSkybox[ eCubeSideFront ]	= poSkyboxElement->Attribute("front");
			m_astrSkybox[ eCubeSideBack ]	= poSkyboxElement->Attribute("back");
			m_astrSkybox[ eCubeSideTop ]	= poSkyboxElement->Attribute("top");
			m_astrSkybox[ eCubeSideBottom ] = poSkyboxElement->Attribute("bottom");


			m_apoSkyboxTex[ eCubeSideLeft ]		= m_poDriver->getTexture( GetAssetPathAndName( m_astrSkybox[ eCubeSideLeft ].c_str(), eGameAssetTypeTexture ) );
			if( m_apoSkyboxTex[ eCubeSideLeft ] )
			{
				m_apoSkyboxTex[ eCubeSideLeft ]->grab();
			}
			m_apoSkyboxTex[ eCubeSideRight ]	= m_poDriver->getTexture( GetAssetPathAndName( m_astrSkybox[ eCubeSideRight ].c_str(), eGameAssetTypeTexture ) );
			if( m_apoSkyboxTex[ eCubeSideRight ] )
			{
				m_apoSkyboxTex[ eCubeSideRight ]->grab();
			}
			m_apoSkyboxTex[ eCubeSideFront ]	= m_poDriver->getTexture( GetAssetPathAndName( m_astrSkybox[ eCubeSideFront ].c_str(), eGameAssetTypeTexture ) );
			if( m_apoSkyboxTex[ eCubeSideFront ] )
			{
				m_apoSkyboxTex[ eCubeSideFront ]->grab();
			}
			m_apoSkyboxTex[ eCubeSideBack ]		= m_poDriver->getTexture( GetAssetPathAndName( m_astrSkybox[ eCubeSideBack ].c_str(), eGameAssetTypeTexture ) );
			if( m_apoSkyboxTex[ eCubeSideBack ] )
			{
				m_apoSkyboxTex[ eCubeSideBack ]->grab();
			}
			m_apoSkyboxTex[ eCubeSideTop ]		= m_poDriver->getTexture( GetAssetPathAndName( m_astrSkybox[ eCubeSideTop ].c_str(), eGameAssetTypeTexture ) );
			if( m_apoSkyboxTex[ eCubeSideTop ] )
			{
				m_apoSkyboxTex[ eCubeSideTop ]->grab();
			}
			m_apoSkyboxTex[ eCubeSideBottom ]	= m_poDriver->getTexture( GetAssetPathAndName( m_astrSkybox[ eCubeSideBottom ].c_str(), eGameAssetTypeTexture ) );
			if( m_apoSkyboxTex[ eCubeSideBottom ] )
			{
				m_apoSkyboxTex[ eCubeSideBottom ]->grab();
			}

			if( m_apoSkyboxTex[ eCubeSideRight ] &&
				m_apoSkyboxTex[ eCubeSideFront ] &&
				m_apoSkyboxTex[ eCubeSideBack ] &&
				m_apoSkyboxTex[ eCubeSideTop ] &&
				m_apoSkyboxTex[ eCubeSideBottom ] )
			{
				m_poSkyBoxNode = m_poSceneMgr->addSkyBoxSceneNode( m_apoSkyboxTex[ eCubeSideLeft ],
							m_apoSkyboxTex[ eCubeSideRight ],
							m_apoSkyboxTex[ eCubeSideFront ],
							m_apoSkyboxTex[ eCubeSideBack ],
							m_apoSkyboxTex[ eCubeSideTop ],
							m_apoSkyboxTex[ eCubeSideBottom ] );
				log_msg( 0, "GameLevelMgr::GameLevelStartup: Skybox was created\n");

			}
			else
			{
				log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR skybox missing one or more textures\n");
			}
		}
	}

	log_msg( 0, "GameLevelMgr::GameLevelStartup loading resources\n" );
	// load level resources
	TiXmlElement * poResourcesElement = poLevelElement->FirstChildElement( "resources" );
	if( poResourcesElement) 
	{
		for(TiXmlElement * poSceneElement = poResourcesElement->FirstChildElement("scene"); poSceneElement != 0; poSceneElement = poSceneElement->NextSiblingElement("mesh")) 
		{
			irr::core::stringc strSceneFile = poSceneElement->Attribute("file");
			if( strSceneFile == "" ) 
			{
				log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find file attribute on scene");
				GameLevelShutdown();
				return 0;
			}
			log_msg( 0, "GameLevelMgr::GameLevelStartup loading scene file %s\n", strSceneFile.c_str() );
			m_poIrrBase->m_poSceneMgr->loadScene( GetAssetPathAndName( strSceneFile.c_str() ) );

			// set graphic params on meshes in the scene
			irr::core::array<irr::scene::ISceneNode *> apoMeshNodes;
			m_poIrrBase->m_poSceneMgr->getSceneNodesFromType( irr::scene::ESNT_MESH, apoMeshNodes );
			for( irr::u32 i = 0; i < apoMeshNodes.size(); i++ ) 
			{
				apoMeshNodes[i]->setMaterialFlag( irr::video::EMF_TRILINEAR_FILTER, UserConfigImp::Instance().m_bTrilinearFiltering );
				for( irr::u32 j = 0; j < apoMeshNodes[i]->getMaterialCount(); j++) 
				{
					for(int k = 0; k < 4; k++) 
					{
						apoMeshNodes[i]->getMaterial(j).TextureLayer[k].AnisotropicFilter = UserConfigImp::Instance().m_iAnisotropicFiltering;
					}
				}
			}
		}

		log_msg( 0, "GameLevelMgr::GameLevelStartup loading collision\n" );
		// load collision
		for(TiXmlElement * poCollisionElement = poResourcesElement->FirstChildElement("collision"); poCollisionElement != 0; poCollisionElement = poCollisionElement->NextSiblingElement("mesh")) 
		{

			// get collision define file
			irr::core::stringc strCollisionScriptFile = poCollisionElement->Attribute("file");
			if( strCollisionScriptFile == "" ) 
			{
				log_msg( 0, "GameLevelStartup: ERROR Could not find file attribute on collision");
				GameLevelShutdown();
				return 0;
			}

			EntTemplate * poEntTemplate = new EntTemplate();
			poEntTemplate->m_strScriptCollisionFile = strCollisionScriptFile;
			poEntTemplate->m_eEntType = eEntityTypeCollision;
			poEntTemplate->m_f32Mass = 0.0f;
			m_apoEntTemplates.push_back(poEntTemplate);

			// Create spawn
			EntSpawnData * poEntSpawnData = new EntSpawnData();
			poEntSpawnData->m_poEntTemplate = poEntTemplate;
			m_apoEntSpawnData.push_back( poEntSpawnData );
		}
		log_msg( 0, "GameLevelMgr::GameLevelStartup loading scripts\n");

		for(TiXmlElement * poScriptElement = poResourcesElement->FirstChildElement("script"); poScriptElement != 0; poScriptElement = poScriptElement->NextSiblingElement("script")) 
		{
			irr::core::stringc strScriptFile = poScriptElement->Attribute("file");
			if( strScriptFile == "" ) 
			{
				log_msg( 0, "GameLevelMgr::GameLevelStartup: ERROR Could not find file attribute on script");
				GameLevelShutdown();
				return 0;
			}
			m_astrScripts.push_back( strScriptFile );
		}
		log_msg( 0, "GameLevelMgr::GameLevelStartup oading scripts done\n");
	}

	log_msg( 0, "GameLevelMgr::GameLevelStartup loading templates\n" );
	// load templates
	TiXmlElement * poTemplatesElement = poLevelElement->FirstChildElement("templates");
	if( poTemplatesElement ) 
	{
		int iNextTemplateId = 1;
		for( TiXmlElement * poTemplateElement = poTemplatesElement->FirstChildElement(); poTemplateElement != 0; poTemplateElement = poTemplateElement->NextSiblingElement() ) 
		{
			EntTemplate * poEntTemplate = new EntTemplate;
			rc = GetEntTemplateProperties( poTemplateElement, * poEntTemplate);
			if( rc )
			{
				log_msg( 0, "GameLevelMgr::GameLevelStartup: templates ERROR %d\n", rc );
				return rc;
			}
			// assign template id
			poEntTemplate->m_s32TemplateId = iNextTemplateId;
			iNextTemplateId++;
			m_apoEntTemplates.push_back(poEntTemplate);
		}
	}

	log_msg( 0, "GameLevelMgr::GameLevelStartup spawning entities\n" );
	// Load entity spawn data
	TiXmlElement * poSpawnElement = poLevelElement->FirstChildElement("objects");
	if( poSpawnElement ) 
	{
		for(TiXmlElement * poCurElement = poSpawnElement->FirstChildElement(); poCurElement != 0; poCurElement = poCurElement->NextSiblingElement()) 
		{
			EntSpawnData * poEntSpawnData = new EntSpawnData();
			rc = GetEntSpawnProperties( poCurElement, *poEntSpawnData);
			if( rc )
			{
				log_msg( 0, "GameLevelMgr::GameLevelStartup: objects ERROR %d\n", rc );
				return rc;
			}
			m_apoEntSpawnData.push_back( poEntSpawnData );
		}
	}
	log_msg( 0, "GameLevelMgr::GameLevelStartup: done\n" );
	return 0;
}
//============================================================================
//! destroy level
RCODE GameLevelMgr::GameLevelShutdown( void )
{
	UnloadLevelResources();
	return 1;
}


//============================================================================
//! get entity template by template name
EntTemplate * GameLevelMgr::GetEntTemplate( const char * pTemplateName )
{
	irr::core::stringc Name = pTemplateName;
	// find template by name
	for( irr::u32 i = 0; i < m_apoEntTemplates.size(); i++) 
	{
		if( m_apoEntTemplates[i]->m_strTemplateName == Name) 
		{
			return m_apoEntTemplates[i];
		}
	}

	return NULL;
}
//============================================================================
//! get entity template by id
EntTemplate * GameLevelMgr::GetEntTemplateById( S32 s32TemplateId )
{
	for( irr::u32 i = 0; i < m_apoEntTemplates.size(); i++) 
	{
		if( m_apoEntTemplates[i]->m_s32TemplateId == s32TemplateId ) 
		{
			return m_apoEntTemplates[i];
		}
	}
	return NULL;
}

//============================================================================
//! read attributes for entity template
RCODE GameLevelMgr::GetEntTemplateProperties( TiXmlElement * poTemplateElement, EntTemplate & oEntTemplate)
{
	TiXmlElement * poCurElement;
	const char * pString;
	int	iTemp;

	// get entity type
	irr::core::stringc strEntType = poTemplateElement->Value();
	// convert entity type description to enum
	if( strEntType == "player" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypePlayer;
	}
	else if( strEntType == "box" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeBox;
	}
	else if( strEntType == "sphere" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeSphere;
	}
	else if( strEntType == "cylinder" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeCylinder;
	}
	else if( strEntType == "zone" ) 
	{
		oEntTemplate.m_eEntType			= eEntityTypeZone;
		oEntTemplate.m_f32Mass			= 0.0f;
		oEntTemplate.m_eCollisionGroup	= ePhysFilterZone;
		oEntTemplate.m_eCollisionMask	= (EPhysFilterTypes)(ePhysFilterRigidBody | ePhysFilterKinematic);
	}
	else if( strEntType == "collision" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeCollision;
	}
	else if( strEntType == "hinge" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeConstraintHinge;
	}
	else if( strEntType == "d6" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeConstraintD6;
	}
	else if( strEntType == "earth" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeEarth;
	}
	else if( strEntType == "cometfire" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeCometFire;
	}
	else if( strEntType == "cometice" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeCometIce;
	}
	else if( strEntType == "asteroidred" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeAsteroidRed;
	}
	else if( strEntType == "asteroidblue" ) 
	{
		oEntTemplate.m_eEntType = eEntityTypeAsteroidBlue;
	}
	else
	{
		log_msg( 0, "GameLevelMgr::GetEntTemplateProperties: unknown entity type %s\n", strEntType.c_str() );
		vx_assert( false );
		return -1;
	}
	poCurElement = poTemplateElement->FirstChildElement("name");
	if( poCurElement ) 
	{
		pString = poCurElement->Attribute("templatename");
		if( pString )
		{
			oEntTemplate.m_strTemplateName = pString;
		}
		else
		{
			log_msg( 0, "GameLevelMgr::GetEntTemplateProperties: ERROR.. missing common name");
			return -1;
		}
		pString = poCurElement->Attribute("commonname");
		if( pString )
		{
			oEntTemplate.m_strEntCommonName = pString;
		}
		else
		{
			log_msg( 0, "GameLevelMgr::GetEntTemplateProperties: ERROR.. missing common name");
			return -1;
		}
	}
	iTemp = 0;
	poTemplateElement->QueryIntAttribute("lifetime", &iTemp );
	oEntTemplate.m_s32LifeTimeMs = iTemp;

	poCurElement = poTemplateElement->FirstChildElement("shape");
	if( poCurElement ) 
	{
		int iType = 0;
		poCurElement->QueryIntAttribute("type", &iType ); 
		oEntTemplate.m_eShapeType = (EPhysGeomType)iType;
		poCurElement->QueryFloatAttribute("w", &oEntTemplate.m_vec3ShapeDimensions[0]);
		poCurElement->QueryFloatAttribute("h", &oEntTemplate.m_vec3ShapeDimensions[1]);
		poCurElement->QueryFloatAttribute("l", &oEntTemplate.m_vec3ShapeDimensions[2]);
		poCurElement->QueryFloatAttribute("r", &oEntTemplate.m_f32Radius);
	}

	oEntTemplate.m_iModelType = 0;
	poCurElement = poTemplateElement->FirstChildElement("mesh");
	if( poCurElement ) 
	{
		pString = poCurElement->Attribute("file");
		if( pString )
		{
			oEntTemplate.m_iModelType = 1;
			oEntTemplate.m_strMeshFile = pString;
			poCurElement->QueryFloatAttribute("scale", &oEntTemplate.m_f32ModelScale);
		}
	}
	else
	{
		// if no mesh then may be a billboard
		poCurElement = poTemplateElement->FirstChildElement("billboard");
		if( poCurElement ) 
		{
			oEntTemplate.m_iModelType = 2;
			poCurElement->QueryFloatAttribute("scale", &oEntTemplate.m_f32ModelScale);
			poCurElement->QueryFloatAttribute("w", &oEntTemplate.m_f32BillboardWidth);
			poCurElement->QueryFloatAttribute("h", &oEntTemplate.m_f32BillboardHeight);
		}
	}
	poCurElement = poTemplateElement->FirstChildElement("physics");
	if( poCurElement ) 
	{
		poCurElement->QueryFloatAttribute("mass", &oEntTemplate.m_f32Mass);
		poCurElement->QueryFloatAttribute("friction", &oEntTemplate.m_f32Friction);
	}
	poCurElement = poTemplateElement->FirstChildElement("damping");
	if(poCurElement) 
	{
		poCurElement->QueryFloatAttribute("linear", &oEntTemplate.m_f32LinearDamping);
		poCurElement->QueryFloatAttribute("angular", &oEntTemplate.m_f32AngularDamping);
	}

	// Get limits for constraints. 0-1 is linear, 2-3 is angular
	poCurElement = poTemplateElement->FirstChildElement("limit");
	if(poCurElement) 
	{
		int iType = 0;
		poCurElement->QueryIntAttribute("type", &iType);
		poCurElement->QueryFloatAttribute("x", &oEntTemplate.m_avec3ConstraintData[iType][0]);
		poCurElement->QueryFloatAttribute("y", &oEntTemplate.m_avec3ConstraintData[iType][1]);
		poCurElement->QueryFloatAttribute("z", &oEntTemplate.m_avec3ConstraintData[iType][2]);
	}

	// Get pivot for constraints
	poCurElement = poTemplateElement->FirstChildElement("pivot");
	if(poCurElement) 
	{
		poCurElement->QueryFloatAttribute("x", &oEntTemplate.m_avec3ConstraintData[0][0]);
		poCurElement->QueryFloatAttribute("y", &oEntTemplate.m_avec3ConstraintData[0][1]);
		poCurElement->QueryFloatAttribute("z", &oEntTemplate.m_avec3ConstraintData[0][2]);
	}

	// Get axis for constraints
	poCurElement = poTemplateElement->FirstChildElement("axis");
	if(poCurElement) 
	{
		poCurElement->QueryFloatAttribute("x", &oEntTemplate.m_avec3ConstraintData[1][0]);
		poCurElement->QueryFloatAttribute("y", &oEntTemplate.m_avec3ConstraintData[1][1]);
		poCurElement->QueryFloatAttribute("z", &oEntTemplate.m_avec3ConstraintData[1][2]);
	}

	poCurElement = poTemplateElement->FirstChildElement("texture");
	if(poCurElement) 
	{
		irr::core::stringc astrAttribute[4];
		
		// Get normal texture
		astrAttribute[0] = poCurElement->Attribute("t1");
		astrAttribute[1] = poCurElement->Attribute("t2");
		astrAttribute[2] = poCurElement->Attribute("t3");
		astrAttribute[3] = poCurElement->Attribute("t4");
		for(int i = 0; i < 4; i++) 
		{
			if(astrAttribute[i] != "") 
			{
				oEntTemplate.m_astrTextures[i] = astrAttribute[i];
			}
		}
	}
	// if mass is zero then is immovable and do not collide with other static bodies but can collide with camera
	if( ( 0.0f == oEntTemplate.m_f32Mass ) && 
		( ePhysFilterRigidBody == oEntTemplate.m_eCollisionGroup ) )
	{
		oEntTemplate.m_eCollisionGroup = ePhysFilterEnviroment;

		// dont collide with other static bodies
		oEntTemplate.m_eCollisionMask = (EPhysFilterTypes)( oEntTemplate.m_eCollisionMask & ~ePhysFilterStatic );
	}
	return 0;
}

//============================================================================
//! xml spawn property reader
RCODE GameLevelMgr::GetEntSpawnProperties( TiXmlElement * poSpawnElement, EntSpawnData& oEntSpawnData )
{
	TiXmlElement * poCurElement;

	// Get name
	oEntSpawnData.m_strEntProperName = poSpawnElement->Attribute("propername");
	if( oEntSpawnData.m_strEntProperName == "" ) 
	{
		log_msg( 0, "GetEntSpawnProperties: ERROR missing proper name\n" );
		return -1;
	}
	// Get template name
	irr::core::stringc strTemplateName = poSpawnElement->Attribute("templatename");
	if( strTemplateName == "" ) 
	{
		log_msg( 0, "GetEntSpawnProperties: ERROR missing template name\n" );
		return -1;
	}

	// Get template data
	EntTemplate * poEntTemplate = GetEntTemplate( strTemplateName.c_str() );
	if( NULL == poEntTemplate ) 
	{
		log_msg( 0, "GetEntSpawnProperties: ERROR couldnt find template %s\n", strTemplateName.c_str());
		return -1;
	}
	oEntSpawnData.m_poEntTemplate = poEntTemplate;
	
	// get position
	poCurElement = poSpawnElement->FirstChildElement("position");
	if(poCurElement) 
	{
		poCurElement->QueryFloatAttribute("x", &oEntSpawnData.m_vec3Position[0] );
		poCurElement->QueryFloatAttribute("y", &oEntSpawnData.m_vec3Position[1] );
		poCurElement->QueryFloatAttribute("z", &oEntSpawnData.m_vec3Position[2] );
	}

	// get rotation
	poCurElement = poSpawnElement->FirstChildElement("rotation");
	if(poCurElement) 
	{
		poCurElement->QueryFloatAttribute("x", &oEntSpawnData.m_vec3Rotation[0] );
		poCurElement->QueryFloatAttribute("y", &oEntSpawnData.m_vec3Rotation[1] );
		poCurElement->QueryFloatAttribute("z", &oEntSpawnData.m_vec3Rotation[2] );
	}

	return 0;
}
//============================================================================
//! create an entity from a spawn date
EntBase * GameLevelMgr::CreateEntitiy( const EntSpawnData &oEntSpawnData ) 
{
	
	// Add object
	EntBase * poNewEntity = NULL;
	switch( oEntSpawnData.m_poEntTemplate->m_eEntType ) 
	{
	case eEntityTypePlayer:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntPlayer( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeBox:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntBox( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeSphere:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntSphere( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeCylinder:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntCylinder( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeZone:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntZone( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeCollision:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntCollision( m_poIrrBase, oEntSpawnData ));
		break;
	//case eEntityTypeConstraintHinge:
	//	poNewEntity = EntMgrImp::Instance().AddEnt(new EntConstraint( m_poIrrBase, oEntSpawnData ));
	//	break;
	//case eEntityTypeConstraintD6:
	//	poNewEntity = EntMgrImp::Instance().AddEnt(new EntConstraint( m_poIrrBase, oEntSpawnData ));
	//	break;
	case eEntityTypeEarth:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntEarth( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeCometFire:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntCometFire( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeCometIce:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntCometIce( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeAsteroidRed:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntAsteroidRed( m_poIrrBase, oEntSpawnData ));
		break;
	case eEntityTypeAsteroidBlue:
		poNewEntity = EntMgrImp::Instance().AddEnt(new EntAsteroidBlue( m_poIrrBase, oEntSpawnData ));
		break;
	default:
		vx_assert( FALSE );
	}
	//m_apoEnts.push_back( poNewEntity );
	vx_assert( poNewEntity );
	return poNewEntity;
}

//============================================================================
//! Creates a constraint from constraint data
EntBase * GameLevelMgr::CreateConstraint( const EntConstraintData& oEntConstraintData ) 
{
	// Add object
	EntBase * poNewConstraint = EntMgrImp::Instance().AddEnt( new EntConstraint( m_poIrrBase, oEntConstraintData ) );
	return poNewConstraint;
}
//============================================================================
//! spawn all of the entities in the level
void GameLevelMgr::SpawnAllEntities() 
{
	for( U32 i = 0; i < m_apoEntSpawnData.size(); i++) 
	{
		EntSpawnData * poEntSpawnData = m_apoEntSpawnData[i];
		CreateEntitiy( *poEntSpawnData );
	}
}

//============================================================================
//! run scripts
void GameLevelMgr::RunScripts( void )
{
}
//============================================================================
//! unload level resources
void GameLevelMgr::UnloadLevelResources( void )
{
	log_msg( 0, "GameLevelMgr::UnloadLevelResources\n");
	m_f32GameVersion = 0.0f;
	m_f32LevelVersion = 0.0f;
	m_astrScripts.clear();			// list of scripts
	for( int i = 0; i < eMaxCubeSide; i++ )
	{
		m_astrSkybox[i] = "";			// list textures for skybox
	}
	log_msg( 0, "GameLevelMgr::UnloadLevelResources: unloading scripts\n" );
	m_astrScripts.clear();
	log_msg( 0, "GameLevelMgr::UnloadLevelResources: unloading templates\n" );
	for( irr::u32 i = 0; i < m_apoEntTemplates.size(); i++ ) 
	{
		delete m_apoEntTemplates[i];
	}
	m_apoEntTemplates.clear();
	log_msg( 0, "GameLevelMgr::UnloadLevelResources: unloading spawn\n" );
	for( irr::u32 i = 0; i < m_apoEntSpawnData.size(); i++ ) 
	{
		delete m_apoEntSpawnData[i];
	}
	m_apoEntSpawnData.clear();
	log_msg( 0, "GameLevelMgr::UnloadLevelResources: unloading skybox\n" );
	/*
	if( m_poSkyBoxNode )
	{
		m_poSkyBoxNode->remove();
	}
	for( int i = 0; i < eMaxCubeSide; i++ )
	{
		// there is a problem if the same texture is used in multiple skybox sides because
		// the texture should not be dropped more than once 
		if( m_apoSkyboxTex[ i ] )
		{
			m_apoSkyboxTex[ i ]->drop();
			m_apoSkyboxTex[ i ] = NULL;
		}
		//if( m_apoSkyboxTex[ i ] )
		//{
		//	for( int j = 0; j < i; j++ )
		//	{
		//		if( m_apoSkyboxTex[ i ] == m_apoSkyboxTex[ j ] )
		//		{
		//			// texture is the same.. dont drop
		//			continue;
		//		}
		//	}
		//}
	}
	*/
	// null the texture pointers
	for( int i = 0; i < eMaxCubeSide; i++ )
	{
		m_apoSkyboxTex[ i ] = NULL;
	}
	// tell ent manager to remove the entities we created
	//for( int i = 0; i < m_apoEnts.size(); i++ )
	//{
	//	EntMgrImp::Instance().DeleteEnt(m_apoEnts[i]);
	//}
	log_msg( 0, "GameLevelMgr::UnloadLevelResources: clearing entity manager\n" );
	EntMgrImp::Instance().ClearEnts();
	//m_apoEnts.clear();
	log_msg( 0, "GameLevelMgr::UnloadLevelResources: done\n" );
}
